/*
 * ULSF Sagittarius IDA
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius;

import sagittarius.airfoil.Airfoil;
import sagittarius.design.Volume;

import json.Json;
import json.ObjectJson;

/**
 * {@link Visualization} input data variables 
 */
public enum Field {

    Length (Min.Length,  Default.Length,  Neutral.Length,  Max.Length,  Inc.Length,  Fmt.Length),
    Radius (Min.Radius,  Default.Radius,  Neutral.Radius,  Max.Radius,  Inc.Radius,  Fmt.Radius),
    Span   (Min.Span,    Default.Span,    Neutral.Span,    Max.Span,    Inc.Span,    Fmt.Span),
    Alpha  (Min.Alpha,   Default.Alpha,   Neutral.Alpha,   Max.Alpha,   Inc.Alpha,   Fmt.Alpha),
    Beta   (Min.Beta,    Default.Beta,    Neutral.Beta,    Max.Beta,    Inc.Beta,    Fmt.Beta),
    Gamma  (Min.Gamma,   Default.Gamma,   Neutral.Gamma,   Max.Gamma,   Inc.Gamma,   Fmt.Gamma),
    Delta  (Min.Delta,   Default.Delta,   Neutral.Delta,   Max.Delta,   Inc.Delta,   Fmt.Delta),
    Epsilon(Min.Epsilon, Default.Epsilon, Neutral.Epsilon, Max.Epsilon, Inc.Epsilon, Fmt.Epsilon),
    Zeta   (Min.Zeta,    Default.Zeta,    Neutral.Zeta,    Max.Zeta,    Inc.Zeta,    Fmt.Zeta),
    Eta    (Min.Eta,     Default.Eta,     Neutral.Eta,     Max.Eta,     Inc.Eta,     Fmt.Eta),
    Theta  (Min.Theta,   Default.Theta,   Neutral.Theta,   Max.Theta,   Inc.Theta,   Fmt.Theta),
    Iota   (Min.Iota,    Default.Iota,    Neutral.Iota,    Max.Iota,    Inc.Iota,    Fmt.Iota),
    Kappa  (Min.Kappa,   Default.Kappa,   Neutral.Kappa,   Max.Kappa,   Inc.Kappa,   Fmt.Kappa);


    public final float min, def, nut, max, inc;

    public final String fmt;


    Field(float min, float def, float nut, float max, float inc, String fmt){
        this.min = min;
        this.def = def;
        this.nut = nut;
        this.max = max;
        this.inc = inc;
        this.fmt = fmt;
    }


    public String format(Visualization data){
        return String.format(this.fmt,data.get(this));
    }


    public final static class Min {

        public final static float Length  = 100.0f;
        public final static float Radius  = 10.0f;
        public final static float Span    = 100.0f;

        public final static float Alpha   =-2.0f;
        public final static float Beta    = 0.0f;
        public final static float Gamma   = 0.0f;
        public final static float Delta   = 0.0f;
        public final static float Epsilon = 0.0f;
        public final static float Zeta    = 0.0f;

        public final static float Eta     = 2.1f;
        public final static float Theta   = 0.0f;
        public final static float Iota    = 0.0f;
        public final static float Kappa   = 0.0f;
    }
    public final static class Default {

        public final static float Length  = Max.Length;
        public final static float Radius  = 150.0f;
        public final static float Span    = 900.0f;

        public final static float Alpha   = 0.5f;
        public final static float Beta    = 8.5f;
        public final static float Gamma   = 3.0f;
        public final static float Delta   = 20.7f;
        public final static float Epsilon = 1.5f;
        public final static float Zeta    = 6.1f;

        public final static float Eta     = 4.3f;
        public final static float Theta   = 0.0f;
        public final static float Iota    = 0.0f;
        public final static float Kappa   = 0.0f;
    }
    public final static class Neutral {

        public final static float Length  = Default.Length;
        public final static float Radius  = Default.Radius;
        public final static float Span    = Default.Span;

        public final static float Alpha   = Min.Alpha;
        public final static float Beta    = Min.Beta;
        public final static float Gamma   = Min.Gamma;
        public final static float Delta   = 1.0f;
        public final static float Epsilon = 1.0f;
        public final static float Zeta    = Min.Zeta;

        public final static float Eta     = 3.0f;
        public final static float Theta   = Min.Theta;
        public final static float Iota    = Min.Iota;
        public final static float Kappa   = Min.Kappa;
    }
    public final static class Max {

        public final static float Length  = 1000.0f;
        public final static float Radius  = 1000.0f;
        public final static float Span    = 1000.0f;

        public final static float Alpha   = 3.0f;
        public final static float Beta    = 20.0f;
        public final static float Gamma   = 20.0f;
        public final static float Delta   = 100.0f;
        public final static float Epsilon = 3.0f;
        public final static float Zeta    = 20.0f;

        public final static float Eta     = 10.0f;
        public final static float Theta   = 3.0f;
        public final static float Iota    = 3.0f;
        public final static float Kappa   = 3.0f;
    }
    public final static class Inc {

        public final static float Length  = 5.0f;
        public final static float Radius  = 5.0f;
        public final static float Span    = 5.0f;

        public final static float Alpha   = 0.1f;
        public final static float Beta    = 0.5f;
        public final static float Gamma   = 1.0f;
        public final static float Delta   = 0.1f;
        public final static float Epsilon = 0.5f;
        public final static float Zeta    = 0.1f;

        public final static float Eta     = 0.1f;
        public final static float Theta   = 0.1f;
        public final static float Iota    = 0.1f;
        public final static float Kappa   = 0.1f;
    }
    public final static class Fmt {

        public final static String Length  = "Length %4.0f";
        public final static String Radius  = "Radius %4.0f";
        public final static String Span    = "Span %4.0f";
        public final static String Alpha   = "Alpha %3.1f";
        public final static String Beta    = "Beta %3.1f";
        public final static String Gamma   = "Gamma %3.0f";
        public final static String Delta   = "Delta %2.1f";
        public final static String Epsilon = "Epsilon %3.1f";
        public final static String Zeta    = "Zeta %3.1f";
        public final static String Eta     = "Eta %2.1f";
        public final static String Theta   = "Theta %2.1f";
        public final static String Iota    = "Iota %2.1f";
        public final static String Kappa   = "Kappa %3.1f";
    }


    public final static float[] Parameters(Visualization data, Field update, float value){
        switch(update){
        case Length:
            return new float[]{
                value,
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Radius:
            return new float[]{
                data.getLength(),
                value,
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Span:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                value,
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Alpha:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                value,
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Beta:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                value,
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Gamma:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                value,
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Delta:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                value,
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Epsilon:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                value,
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Zeta:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                value,
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Eta:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                value,
                data.getTheta(),
                data.getIota(),
                data.getKappa()
            };
        case Theta:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                value,
                data.getIota(),
                data.getKappa()
            };
        case Iota:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                value,
                data.getKappa()
            };
        case Kappa:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                data.getAlpha(),
                data.getBeta(),
                data.getGamma(),
                data.getDelta(),
                data.getEpsilon(),
                data.getZeta(),
                data.getEta(),
                data.getTheta(),
                data.getIota(),
                value,
            };
        default:
            throw new Error(update.name());
        }
    }
    public final static float[] Sets(Visualization data, Values values){
        switch(values){
        case Defaults:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                Field.Default.Alpha,
                Field.Default.Beta,
                Field.Default.Gamma,
                Field.Default.Delta,
                Field.Default.Epsilon,
                Field.Default.Zeta,
                Field.Default.Eta,
                Field.Default.Theta,
                Field.Default.Iota,
                Field.Default.Kappa
            };
        case Neutrals:
            return new float[]{
                data.getLength(),
                data.getRadius(),
                data.getSpan(),
                Field.Neutral.Alpha,
                Field.Neutral.Beta,
                Field.Neutral.Gamma,
                Field.Neutral.Delta,
                Field.Neutral.Epsilon,
                Field.Neutral.Zeta,
                Field.Neutral.Eta,
                Field.Neutral.Theta,
                Field.Neutral.Iota,
                Field.Neutral.Kappa
            };
        default:
            throw new Error(values.name());
        }
    }
    public final static float[] Decode(Json json){
        if (null != json && json.isObject()){

            Json sagittarius = json.at("sagittarius");

            if (null != sagittarius && sagittarius.isObject()){

                Version version = (Version)sagittarius.getValue("version",Version.class);

                if (null != version && version.hasNumbers()){

                    Json design = sagittarius.at("design");

                    if (null != design && design.isObject()){

                        Json airfoil = design.at("airfoil");

                        if (null != airfoil && airfoil.isObject()){
                            /*
                             * Simple option (since 1.0.6)
                             */
                            String model = (String)airfoil.getValue("model",String.class);
                            if (null != model){

                                Airfoil.Configuration(model);
                            }
                            String identifier = (String)airfoil.getValue("identifier",String.class);
                            if (null != identifier){

                                Airfoil.Identifier(identifier);
                            }
                        }

                        Json body = design.at("body");

                        if (null != body && body.isObject()){
                            /*
                             * File Format Requirements
                             */
                            String volume = (String)body.getValue("volume",String.class);
                            Float length  = (Float)body.getValue("length",Float.class);
                            Float radius  = (Float)body.getValue("radius",Float.class);
                            Float span    = (Float)body.getValue("span",Float.class);
                            Float alpha   = (Float)body.getValue("alpha",Float.class);
                            Float beta    = (Float)body.getValue("beta",Float.class);
                            Float gamma   = (Float)body.getValue("gamma",Float.class);
                            Float delta   = (Float)body.getValue("delta",Float.class);
                            Float epsilon = (Float)body.getValue("epsilon",Float.class);
                            Float zeta    = (Float)body.getValue("zeta",Float.class);
                            Float eta     = (Float)body.getValue("eta",Float.class);
                            Float theta   = (Float)body.getValue("theta",Float.class);
                            Float iota    = (Float)body.getValue("iota",Float.class);
                            Float kappa   = (Float)body.getValue("kappa",Float.class);

                            if (null != volume
                                && null != length && null != radius && null != span
                                && null != alpha && null != beta && null != gamma
                                && null != delta && null != epsilon && null != zeta
                                && null != eta && null != theta && null != iota && null != kappa){

                                Volume.Configuration(volume);

                                return new float[]{
                                    length,
                                    radius,
                                    span,
                                    alpha,
                                    beta,
                                    gamma,
                                    delta,
                                    epsilon,
                                    zeta,
                                    eta,
                                    theta,
                                    iota,
                                    kappa
                                };
                            }
                            else
                                throw new IllegalStateException(String.format("Incomplete JSON missing parameters%n%s",json.toString(10)));
                        }
                        else
                            throw new IllegalStateException(String.format("Incomplete JSON missing 'body'%n%s",json.toString(10)));
                    }
                    else
                        throw new IllegalStateException(String.format("Incomplete JSON missing 'design'%n%s",json.toString(10)));
                }
                else
                    throw new IllegalStateException(String.format("Incomplete JSON missing 'version'%n%s",json.toString(10)));
            }
            else
                throw new IllegalStateException(String.format("Incomplete JSON missing 'sagittarius'%n%s",json.toString(10)));
        }
        else
            throw new IllegalStateException("Missing JSON object");
    }
    public final static Json Encode(Visualization data){

        ObjectJson top = new ObjectJson();

        ObjectJson sagittarius = new ObjectJson();
        top.set("sagittarius",sagittarius);

        sagittarius.set("version",Version.Number);

        ObjectJson design = new ObjectJson();
        sagittarius.set("design",design);

        ObjectJson airfoil = new ObjectJson();
        design.set("airfoil",airfoil);

        airfoil.set("model",data.getAirfoilConfiguration());
        airfoil.set("identifier",data.getAirfoilIdentifier());

        ObjectJson body = new ObjectJson();
        design.set("body",body);

        body.set("volume",data.getVolume());
        body.setValue("length",data.getLength());
        body.setValue("radius",data.getRadius());
        body.setValue("span",data.getSpan());
        body.setValue("alpha",data.getAlpha());
        body.setValue("beta",data.getBeta());
        body.setValue("gamma",data.getGamma());
        body.setValue("delta",data.getDelta());
        body.setValue("epsilon",data.getEpsilon());
        body.setValue("zeta",data.getZeta());
        body.setValue("eta",data.getEta());
        body.setValue("theta",data.getTheta());
        body.setValue("iota",data.getIota());
        body.setValue("kappa",data.getKappa());

        return top;
    }
}
